package main.java.LeetCode.ClassicalAlgorithm.Worker.Impl.event;

import main.java.LeetCode.ClassicalAlgorithm.Queue.Impl.BlockQueue;
import main.java.LeetCode.ClassicalAlgorithm.Queue.Queue;
import main.java.LeetCode.ClassicalAlgorithm.Worker.FutureTask;
import main.java.LeetCode.ClassicalAlgorithm.Worker.Task;

import java.util.HashSet;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;

public class EventLoopGroup {

    private final int coreSize;
    private final int maxSize;
    private final Queue<Task> queue;
    private final long keepAlive;
    private final TimeUnit unit;
    private final ReentrantLock mainLock = new ReentrantLock();

    private final AtomicInteger warmFactory;

    private final HashSet<EventLoop> eventLoops = new HashSet<>();

    private final AtomicInteger eventCounts = new AtomicInteger(0);
    public EventLoopGroup(int coreSize, int maxSize, Queue<Task> queue, long keepAlive, TimeUnit unit,int warmFactory){
        this.coreSize = coreSize;
        this.maxSize = maxSize;
        this.queue = queue;
        this.keepAlive = keepAlive;
        this.unit = unit;
        this.warmFactory = new AtomicInteger(warmFactory);
    }
    public EventLoopGroup(int coreSize,int maxSize){
        this(coreSize,maxSize,new BlockQueue<>(),5,TimeUnit.SECONDS,10);
    }

    public boolean submit(Task task){
        if(task==null)
            throw new RuntimeException("任务为空");

        int count = eventCounts.get();

        if(count<coreSize){
            if(addEventLoop(task,true))
                return true;
        }

        if(queue.offer(task))
            return true;

        boolean isAdd = addEventLoop(task,false);

        warmUp();

        return isAdd;
    }

    public <E> FutureResult<E> execute(FutureTask<E> futureTask){
        EventFutureTask<E> eventFutureTask = new EventFutureTask<>(futureTask);
        this.submit(eventFutureTask);
        return eventFutureTask.getResult();
    }

    public boolean addEventLoop(Task task,boolean isCore){

        for(;;){
            int count = eventCounts.get();
            if(isCore?count>=coreSize:count>=maxSize)
                return false;
            if(eventCounts.compareAndSet(count,count+1)){
                break;
            }
        }

        try {
            mainLock.lock();
            EventLoop eventLoop = new EventLoop(queue, isCore, keepAlive, unit);

            eventLoop.setFirstTask(task);
            eventLoop.setEventCounts(eventCounts);

            eventLoops.add(eventLoop);
            eventLoop.go();
        } finally {
            mainLock.unlock();
        }
        return true;
    }

    public void shutdown(){
        mainLock.lock();
        try {
            for(EventLoop eventLoop:eventLoops){
                if(!eventLoop.getThread().isInterrupted())
                    eventLoop.getThread().interrupt();
            }
        }catch (SecurityException ignore){

        }finally {
            mainLock.unlock();
        }
    }

    private void warmUp(){
        if(warmFactory.get()==0)
            return;
        int w = warmFactory.getAndDecrement();
        if(w>0){
            try{
                Thread.sleep(1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    private static class EventFutureTask<E> implements Task{

        public FutureResult<E> futureResult;
        private final FutureTask<E> futureTask;

        public EventFutureTask(FutureTask<E> futureTask){
            this.futureResult = new FutureResult<>();
            this.futureTask = futureTask;
        }

        @Override
        public void doTask() throws InterruptedException {
            E e = futureTask.doTask();
            futureResult.setResult(e);
        }

        public FutureResult<E> getResult(){
            return this.futureResult;
        }
    }
}
